/**
******************************************************************************************
* @file LedsHandyman.c
* @brief Management of the leds boards with the io-16 bricklet.
* @date 7 mai 2015
* @author Benjamin Lancinot
*****************************************************************************************/
/* COPYRIGHT ****************************************************************************
*****************************************************************************************
* Copyright (c) 2015, Audrey LOISEAU and Benjamin LANCINOT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
*    list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
********************************************************************************************/

#include "LedsHandyman.h"

/*******************************************************************************************
* PROTOTYPES
********************************************************************************************/

void LedsHandyman_destroy(LedsHandyman **);

void LedsHandyman_allOrange(IO16 *);

void LedsHandyman_allGreen(IO16 *);

void LedsHandyman_allRed(IO16 *);

void LedsHandyman_allSwitchOff(IO16 *);

void LedsHandyman_switchLedByLedOnTime(IO16 *,uint8_t*);

void LedsHandyman_tinkerBulbs(LedsHandyman *, uint8_t*);

void * launchProgressSwitchOff(LedsHandyman*);

void * launchFirstAnimation(LedsHandyman*);

void  LedsHandyman_destroyUtilities(LedsHandyman*);

/*******************************************************************************************
* FUNCTIONS
********************************************************************************************/

/**
 * @brief Constructor
 * @param io io-16 bricklet use for pilot leds board
 * @return Pointer to a new LedsHandyman
 **/
LedsHandyman* LedsHandyman_create(IO16 io){
	LedsHandyman* This = ( LedsHandyman*) malloc(sizeof(*This));

	This->io = io;
        This->animationInProgress = FALSE;

        //Init functions
        This->destroy = LedsHandyman_destroy;
	This->tinkerBulbs =  LedsHandyman_tinkerBulbs;

        int i;
        for (i = 0; i < 2; i++) {
		This->animThread[i] = 0;
	}

	return This;
}


/**
 * @brief Destroy all of the components of LedsHandyman
 * @param This Pointer to the wanted LedsHandyman
 **/
void  LedsHandyman_destroyUtilities(LedsHandyman* This){

        //switch off the led and join threads if its active
        LedsHandyman_tinkerBulbs(This, NULL);

}

/**
 * @brief Destructor
 * @param This Double pointer to the wanted LedsHandyman
 **/
void LedsHandyman_destroy(LedsHandyman **This){
        LedsHandyman_destroyUtilities(*This);
        if (*This) {
        	free(*This);
		*This = NULL;
	}
}

/**
 * @brief Turn on the green all of leds
 * @param io Pointer to the io-16 bricklet use for pilot leds board
 **/
void LedsHandyman_allGreen(IO16* io){
	io16_set_port(io, 'a',85);
	io16_set_selected_values(io, 'b',3,1);
}

/**
 * @brief Turn on the red all of leds
 * @param io Pointer to the io-16 bricklet use for pilot leds board
 **/
void LedsHandyman_allRed(IO16 *io){
	io16_set_port(io, 'a',170);
	io16_set_selected_values(io, 'b',3,2);
}

/**
 * @brief Turn on the orange all of leds
 * @param io Pointer to the io-16 bricklet use for pilot leds board
 **/
void LedsHandyman_allOrange(IO16 *io){
	io16_set_port(io, 'a',255);
        io16_set_selected_values(io, 'b',3,3);
}

/**
 * @brief Turn off all of leds
 * @param io Pointer to the io-16 bricklet use for pilot leds board
 **/
void LedsHandyman_allSwitchOff(IO16 *io){
	io16_set_port(io, 'a',0);
	io16_set_selected_values(io, 'b',3,0);
}

/**
 * @brief Pilot leds one by one with a char sequence. The time is already pilot by this sequence
 * @param io Pointer to the io-16 bricklet use for pilot leds board
 * @param sequence Sequence for decide color of led one by one and the time of this sequence
 **/
void LedsHandyman_switchLedByLedOnTime(IO16* io, uint8_t* sequence){
        uint8_t total;
        total = (uint8_t)(sequence[4]+ sequence[3]*4+ sequence[2]*16+ sequence[1]*64);
        if(sequence[6] != 0){
		io16_set_port_monoflop(io, 'a', total, total, sequence[6]*FIFTEEN_SECOND);
		io16_set_port_monoflop(io, 'b', sequence[5], sequence[5], sequence[6]*FIFTEEN_SECOND);
        }else{
		io16_set_selected_values(io, 'a', total, total);
		io16_set_selected_values(io, 'b',sequence[5], sequence[5]);
        }

}

/**
 * @brief Synchronize leds display, animation. New state with leds must go through it
 * @param This Pointer to the wanted LedsHandyman
 * @param partition Char sequence with action to do and argument for it
 **/
void LedsHandyman_tinkerBulbs(LedsHandyman *This, uint8_t* partition){
	static int animationNumber;

	//Cancel thread if any else are active
	if(This->animationInProgress == TRUE && (animationNumber == 0 || animationNumber == 1) ){
		This->animationInProgress = FALSE;
		pthread_join(This->animThread[animationNumber],NULL);
	}

	//Switch off all leds
	io16_set_selected_values(&This->io, 'a', 255,0);
	io16_set_selected_values(&This->io, 'b', 3,0);

	if(partition != NULL ){
		switch (partition[0]) {
		case 0:
			animationNumber =-1;
			LedsHandyman_allSwitchOff(&This->io);
			break;
		case 1:
			animationNumber =-1;
			LedsHandyman_allGreen(&This->io);
			break;
		case 2:
			animationNumber =-1;
			LedsHandyman_allRed(&This->io);
			break;
		case 3:
			animationNumber =-1;
			LedsHandyman_allOrange(&This->io);
			break;
		case 4:
			animationNumber =-1;
			LedsHandyman_switchLedByLedOnTime(&This->io,partition);
			break;
		case 5:
			animationNumber =0;
			This->animationInProgress = TRUE;
			pthread_create(&This->animThread[0], 0,(void *) &launchProgressSwitchOff,(LedsHandyman *) This);
			break;
		case 6:
			animationNumber =1;
			This->animationInProgress = TRUE;
			pthread_create(&This->animThread[1], 0,(void *) &launchFirstAnimation,(LedsHandyman *) This);
			break;
		default:
			break;
		}

	}

}


/**
 * @brief Animation with leds. The leds switch off progressively.
 * @param This Pointer to the wanted LedsHandyman
 * @return value for the thread development
 **/
void * launchProgressSwitchOff(LedsHandyman * This){

        //all in red
	int state =0;
	io16_set_port(&This->io, 'a', 170);
	io16_set_selected_values(&This->io, 'b', 3,2);

        usleep(2000000);
        while (This->animationInProgress) {

		switch (state) {
			//Progressive green
		case 0:
			io16_set_selected_values(&This->io, 'a', 204,68);
			break;
		case 1:
			io16_set_selected_values(&This->io, 'a', 51,17);
			break;
		case 2:
			io16_set_selected_values(&This->io, 'b', 3, 1);
			break;

			//Progressive orange
		case 3:
			io16_set_selected_values(&This->io, 'a', 136,136);
			break;
		case 4:
			io16_set_selected_values(&This->io, 'a', 34 ,34);
			break;
		case 5:
			io16_set_selected_values(&This->io, 'b', 3, 3);
			break;

			//Progressive dark
		case 6:
			io16_set_selected_values(&This->io, 'a',204,0);
			break;
		case 7:
			io16_set_selected_values(&This->io, 'a', 51,0);
			break;
		case 8:
			io16_set_selected_values(&This->io, 'b', 3, 0);
			break;
		default:
			break;
		}

                if(state == 8){
			This->animationInProgress = FALSE;
                }else{
			state++;
			usleep(2000000);
                }
	}
	return NULL;
}


/**
 * @brief Another animation with leds.
 * @param This Pointer to the wanted LedsHandyman
 * @return value for the thread development
 **/
void * launchFirstAnimation(LedsHandyman * This){

        //all in red
	int state =0;

	io16_set_port(&This->io, 'a', 0);
	io16_set_selected_values(&This->io, 'b',3, 0);
        usleep(500000);
        while (This->animationInProgress) {
                switch (state) {

			//Progressive red on dark
		case 0:
			io16_set_selected_values(&This->io, 'a', 8, 8);
			break;
		case 1:
			io16_set_selected_values(&This->io, 'a', 40,32);
			break;
		case 2:
			io16_set_selected_values(&This->io, 'a', 160, 128);
			break;
		case 3:
			io16_set_selected_values(&This->io, 'a', 130,2);
			break;
		case 4:
			io16_set_selected_values(&This->io, 'a', 2 ,0);
			io16_set_selected_values(&This->io, 'b', 2,2);
			break;

			//Progressive orange on dark
		case 5:
			io16_set_selected_values(&This->io, 'b', 3, 3);
			break;
		case 6:
			io16_set_selected_values(&This->io, 'b', 3, 0);
			io16_set_selected_values(&This->io, 'a', 3, 3);
			break;
		case 7:
			io16_set_selected_values(&This->io, 'a', 195,192);
			break;
		case 8:
			io16_set_selected_values(&This->io, 'a', 240, 48);
			break;
		case 9:
			io16_set_selected_values(&This->io, 'a', 60,12);
			break;

			//Progressive green on dark
		case 10:
			io16_set_selected_values(&This->io, 'a',  12, 4);
			break;
		case 11:
			io16_set_selected_values(&This->io, 'a', 20,16);
			break;
		case 12:
			io16_set_selected_values(&This->io, 'a', 80, 64);
			break;
		case 13:
			io16_set_selected_values(&This->io, 'a', 65,1);
			break;
		case 14:
			io16_set_selected_values(&This->io, 'a', 1 ,0);
			io16_set_selected_values(&This->io, 'b', 1,1);
			break;

			//Progressive green
		case 15:
			io16_set_selected_values(&This->io, 'a',  1,1);
			break;
		case 16:
			io16_set_selected_values(&This->io, 'a', 64,64);
			break;
		case 17:
			io16_set_selected_values(&This->io, 'a', 16, 16);
			break;
		case 18:
			io16_set_selected_values(&This->io, 'a', 4,4);
			break;

			//Progressive orange on green
		case 19:
			io16_set_selected_values(&This->io, 'a',  8, 8);
			break;
		case 20:
			io16_set_selected_values(&This->io, 'a', 32,32);
			break;
		case 21:
			io16_set_selected_values(&This->io, 'a', 128, 128);
			break;
		case 22:
			io16_set_selected_values(&This->io, 'a', 2,2);
			break;
		case 23:
			io16_set_selected_values(&This->io, 'b', 2,2);
			break;

			//Progressive red on orange
		case 24:
			io16_set_selected_values(&This->io, 'b', 1,0);
			break;
		case 25:
			io16_set_selected_values(&This->io, 'a', 1,0);
			break;
		case 26:
			io16_set_selected_values(&This->io, 'a',  64, 0);
			break;
		case 27:
			io16_set_selected_values(&This->io, 'a', 16,0);
			break;
		case 28:
			io16_set_selected_values(&This->io, 'a', 4, 0);
			break;


			//Progressive dark on red
		case 29:
			io16_set_selected_values(&This->io, 'a', 8,0);
			break;
		case 30:
			io16_set_selected_values(&This->io, 'a', 32,0);
			break;
		case 31:
			io16_set_selected_values(&This->io, 'a',  128, 0);
			break;
		case 32:
			io16_set_selected_values(&This->io, 'a', 2,0);
			break;
		case 33:
			io16_set_selected_values(&This->io, 'b', 2, 0);
			break;
		default:
			break;
		}

		if(state == 33){
			This->animationInProgress = FALSE;
                }else{
			state++;
			usleep(500000);
                }
        }
	return NULL;
}
